﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net.Mail;
using System.Security.Cryptography;
using System.Text;
using System.Web.Mvc;
using System.Web.Security;
using SportMeetingPlanner.Models;
using SportMeetingPlanner.ViewModels;

namespace SportMeetingPlanner.Controllers
{
    public class AccountController : Controller
    {
        SportMeetingEntities entities = new SportMeetingEntities();
        SmtpClient smtp;

        public AccountController()
            : base()
        {
            smtp = new SmtpClient();
        }

        //
        // GET: /Account/LogOn
        [RequireHttps]
        public ActionResult LogOn()
        {
            return View();
        }

        [HttpPost]
        [RequireHttps]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                if (ValidateUser(model.UserName, model.Password))
                {
                    FormsAuthentication.SetAuthCookie(model.UserName, false);
                    if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Home");
                    }
                }
                else
                {
                    ModelState.AddModelError("LogOnModel", "The user name or password provided is incorrect.");
                }
            }

            //If something is wrong, redisplay the form
            return View(model);
        }

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();

            return RedirectToAction("Index", "Home");
        }

        /// <summary>
        /// For displaying the page for requesting a new password
        /// </summary>
        /// <returns></returns>
        [RequireHttps]
        public ActionResult ForgotPassword()
        {
            return View();
        }

        [HttpPost]
        [RequireHttps]
        public ActionResult ForgotPassword(ForgotPasswordModel model)
        {
            if (ModelState.IsValid)
            {
                List<User> users = entities.Users.Where(u => u.UserName == model.UserName).ToList();
                if (users != null && users.Count > 0)
                {
                    if (entities.Tokens.Where(t => t.UserName == model.UserName).Count() == 0)
                    {
                        String newPass = Membership.GeneratePassword(16, 4);
                        Token changePasswordToken = new Token { UserName = users.First().UserName, TokenString = newPass, DateCreated = DateTime.Now };
                        entities.Tokens.Add(changePasswordToken);
                        entities.SaveChanges();

                        MailMessage mm = new MailMessage();
                        mm.Body = string.Format("You have received this message because someone provided your username for the ForgotPassword form in the Sport Meeting Planner application. A random token has been generated for you:\r\n{0}\r\nYou can use this token to change your existing password on the following page:\r\n{1}\r\nIf this was a mistake and you didn't request the password change then you can ignore this message since the password itself will not be changed until you explicitly do so.\n\rThe token will be invalidated after 24 hours.", newPass, Url.Action("ChangePassword", "Account", null, Request.Url.Scheme, null));
                        mm.Subject = "[SMP] Change password";
                        mm.From = new MailAddress("smpadmin@fortech.ro");
                        mm.To.Add(new MailAddress(users.First().Email));
                        smtp.SendAsync(mm, null);

                        return RedirectToAction("PasswordRequestSuccessfulMessage", "Messages");
                    }
                    else
                    {
                        ModelState.AddModelError("ForgotPasswordModel", "User has another password request pending");
                    }
                }
                else
                {
                    ModelState.AddModelError("ForgotPasswordModel", "The provided username does not exist");
                }
            }

            return View(model);
        }

        /// <summary>
        /// Page for changing a user's password
        /// </summary>
        /// <returns></returns>
        public ActionResult ChangePassword()
        {
            return View();
        }

        /// <summary>
        /// Action for changing a user's password
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult ChangePassword(ChangePasswordModel model)
        {
            if (ModelState.IsValid)
            {
                List<Token> tokens = entities.Tokens.Where(m => m.TokenString == model.PasswordToken).ToList();
                if (tokens.Count() == 1)
                {
                    Token token = tokens.First();
                    User user = entities.Users.Single(u => u.UserName == token.UserName);
                    var passData = System.Text.Encoding.ASCII.GetBytes(model.NewPassword);
                    string hashedPassword = Convert.ToBase64String(MD5.Create().ComputeHash(passData));
                    user.Password = hashedPassword;
                    entities.Tokens.Remove(tokens.First());
                    entities.Entry(user).State = EntityState.Modified;
                    entities.SaveChanges();

                    return RedirectToAction("PasswordChangedSuccessfullyMessage", "Messages");
                }
                else
                {
                    ModelState.AddModelError("ChangePasswordModel", "There is no password change request for the token");
                }
            }

            return View(model);
        }

        private bool ValidateUser(string userName, string passWord)
        {
            try
            {
                User user = entities.Users.Single(u => u.UserName == userName);
                var passData = System.Text.Encoding.ASCII.GetBytes(passWord);
                string hashedPassword = Convert.ToBase64String(MD5.Create().ComputeHash(passData));
                if (hashedPassword == user.Password)
                {
                    return true;
                }
            }
            catch (ArgumentNullException)
            {
                return false;
            }
            catch (InvalidOperationException)
            {
                return false;
            }
            catch (EncoderFallbackException)
            {
                return false;
            }

            return false;
        }

    }
}
